﻿using IP2Region.Net.Abstractions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Shyjus.BrowserDetection;
using System.ComponentModel;
using Zhp.Common.Helper;
using Zhp.Common.WebApp;
using Zhp.Entity.Monitor;

namespace Zhp.Api.Filters
{
    public class ExceptionFilter : IAsyncExceptionFilter
    {
        private readonly ILogger _logger;
        private readonly ICurrentUser _currentUser;
        private readonly ISearcher _ipSearcher;
        private readonly IBrowserDetector _browser;

        public ExceptionFilter(ILogger<ExceptionFilter> logger, ICurrentUser currentUser, ISearcher searcher, IBrowserDetector browser)
        {
            _logger = logger;
            _currentUser = currentUser;
            _ipSearcher = searcher;
            _browser = browser;
        }

        public async Task OnExceptionAsync(ExceptionContext context)
        {
            if (!context.ExceptionHandled)
            {
                var result = new ObjectResult(context.Exception.Message);
                result.StatusCode = StatusCodes.Status500InternalServerError;

                context.Result = result;

                try
                {
                    await InsertLog(context);

                    _logger.LogError(WriteLog(context));
                }
                catch (Exception ex)
                {
                    ConsoleHelper.WriteLine($"异常日志记录失败：失败原因：{ex.Message}，异常原因：{context.Exception.Message}", ConsoleColor.Red);
                }
            }

            context.ExceptionHandled = true;
        }

        private async Task InsertLog(ExceptionContext context)
        {
            var httpContext = context.HttpContext;
            var args = await httpContext.GetAllRequestParams();
            var remoteIp = httpContext.GetIP();
            var desc = ((ControllerActionDescriptor)context.ActionDescriptor).MethodInfo.GetCustomAttributes(typeof(DescriptionAttribute), true).OfType<DescriptionAttribute>().FirstOrDefault();
            var log = new ExceptionLog
            {
                Controller = httpContext.GetControllerName(),
                Action = httpContext.GetActionName(),
                Method = httpContext.Request.Method,
                Description = desc?.Description,
                RequestUrl = httpContext.Request.Path,
                RequestParameters = args.ToJson(),
                ExceptionMessage = context.Exception.Message,
                ExceptionInner = context.Exception.InnerException?.StackTrace,
                ExceptionStack = context.Exception.StackTrace,
                RequestIp = remoteIp,
                IpAddress = _ipSearcher.Search(remoteIp),
                OperatingSystem = _browser.Browser?.OS,
                DeviceType = _browser.Browser?.DeviceType,
                BrowserName = _browser.Browser?.Name,
                Version = _browser.Browser?.Version,
            };

            await Task.Run(async() => await log.InsertAsync());
        }

        /// <summary>
        /// 自定义返回格式
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private string WriteLog(ExceptionContext context)
        {
            var httpContext = context.HttpContext;
            var args = httpContext.GetAllRequestParams().Result;
            var remoteIp = httpContext.GetIP();
            return $"【异常信息】 : {context.Exception.Message} \r\n" +
                   $"【异常类型】 : {context.Exception.GetType().Name} \r\n" +
                   $"【请求路径】 : {httpContext.Request.Path} \r\n" +
                   $"【请求类型】 : {httpContext.Request.Method} \r\n" +
                   $"【请求内容】 : {httpContext.GetAllRequestParams().Result} \r\n" +
                   $"【当前用户】 : {_currentUser.Name} \r\n" +
                   $"【当前IP】  : {remoteIp} \r\n" +
                   $"【IP地址】  : {_ipSearcher.Search(remoteIp)} \r\n" +
                   $"【操作系统】  : {_browser.Browser?.OS} \r\n" +
                   $"【设备类型】  : {_browser.Browser?.DeviceType} \r\n" +
                   $"【浏览器】  : {_browser.Browser?.Name} \r\n" +
                   $"【版本号】  : {_browser.Browser?.Version} \r\n" +
                   $"【堆栈调用】 ：{context.Exception.StackTrace} \r\n";
        }
    }
}
